/*******************************************************************************
 * Copyright 2008 www.softao.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.softao.ming.gwt.user.client;

import com.google.gwt.user.client.ui.Widget;

/**
 * Range rule validates the value of the widget within a range. Currently, it
 * supports validation on Double, Float, Long, Integer and String.
 * 
 * @author Dop Sun Jul 15, 2008
 * @param <T>
 *            the range value type
 */
public class RangeRule<T extends Comparable<T>> implements ValidationRule {
	/**
	 * @param right
	 * @param rightIncluded
	 * @return less range check for the integer values
	 */
	public static final RangeRule<Integer> less(int right, boolean rightIncluded) {
		return less(WidgetReaders.getIntegerReader(), right, rightIncluded);
	}

	/**
	 * @param right
	 * @param rightIncluded
	 * @return less range check for the long values
	 */
	public static final RangeRule<Long> less(long right, boolean rightIncluded) {
		return less(WidgetReaders.getLongReader(), right, rightIncluded);
	}

	/**
	 * @param right
	 * @param rightIncluded
	 * @return less range check for the float values
	 */
	public static final RangeRule<Float> less(float right, boolean rightIncluded) {
		return less(WidgetReaders.getFloatReader(), right, rightIncluded);
	}

	/**
	 * @param right
	 * @param rightIncluded
	 * @return less range check for the double values
	 */
	public static final RangeRule<Double> less(double right,
			boolean rightIncluded) {
		return less(WidgetReaders.getDoubleReader(), right, rightIncluded);
	}

	/**
	 * @param right
	 * @param rightIncluded
	 * @return less range check for the integer values
	 */
	public static final RangeRule<String> less(String right,
			boolean rightIncluded) {
		return less(WidgetReaders.getStringReader(), right, rightIncluded);
	}

	/**
	 * @param <T>
	 * @param widgetReader
	 *            read the value from the widget.
	 * @param right
	 * @param rightIncluded
	 * @return rule validates the value less than <code>right</code> or equal to
	 *         (if <code>rightIncluded</code> is <code>true</code>).
	 */
	public static final <T extends Comparable<T>> RangeRule<T> less(
			WidgetReader<T> widgetReader, T right, boolean rightIncluded) {
		return between(widgetReader, null, false, right, rightIncluded);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @return great range check for the integer values
	 */
	public static final RangeRule<Integer> great(int left, boolean leftIncluded) {
		return great(WidgetReaders.getIntegerReader(), left, leftIncluded);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @return great range check for the long values
	 */
	public static final RangeRule<Long> great(long left, boolean leftIncluded) {
		return great(WidgetReaders.getLongReader(), left, leftIncluded);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @return great range check for the float values
	 */
	public static final RangeRule<Float> great(float left, boolean leftIncluded) {
		return great(WidgetReaders.getFloatReader(), left, leftIncluded);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @return great range check for the double values
	 */
	public static final RangeRule<Double> great(double left,
			boolean leftIncluded) {
		return great(WidgetReaders.getDoubleReader(), left, leftIncluded);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @return great range check for the String values
	 */
	public static final RangeRule<String> great(String left,
			boolean leftIncluded) {
		return great(WidgetReaders.getStringReader(), left, leftIncluded);
	}

	/**
	 * @param <T>
	 * @param widgetReader
	 *            read the value from the widget.
	 * @param left
	 * @param leftIncluded
	 * @return rule validates the value greater than <code>left</code> or equal
	 *         to (if <code>leftIncluded</code> is <code>true</code>).
	 */
	public static final <T extends Comparable<T>> RangeRule<T> great(
			WidgetReader<T> widgetReader, T left, boolean leftIncluded) {
		return between(widgetReader, left, leftIncluded, null, false);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @param right
	 * @param rightIncluded
	 * @return great range check for the Integer values
	 */
	public static final RangeRule<Integer> between(int left,
			boolean leftIncluded, int right, boolean rightIncluded) {
		return between(WidgetReaders.getIntegerReader(), left, leftIncluded,
				null, false);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @param right
	 * @param rightIncluded
	 * @return great range check for the long values
	 */
	public static final RangeRule<Long> between(long left,
			boolean leftIncluded, long right, boolean rightIncluded) {
		return between(WidgetReaders.getLongReader(), left, leftIncluded, null,
				false);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @param right
	 * @param rightIncluded
	 * @return great range check for the float values
	 */
	public static final RangeRule<Float> between(float left,
			boolean leftIncluded, float right, boolean rightIncluded) {
		return between(WidgetReaders.getFloatReader(), left, leftIncluded,
				null, false);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @param right
	 * @param rightIncluded
	 * @return great range check for the double values
	 */
	public static final RangeRule<Double> between(double left,
			boolean leftIncluded, double right, boolean rightIncluded) {
		return between(WidgetReaders.getDoubleReader(), left, leftIncluded,
				null, false);
	}

	/**
	 * @param left
	 * @param leftIncluded
	 * @param right
	 * @param rightIncluded
	 * @return great range check for the string values
	 */
	public static final RangeRule<String> between(String left,
			boolean leftIncluded, String right, boolean rightIncluded) {
		return between(WidgetReaders.getStringReader(), left, leftIncluded,
				null, false);
	}

	/**
	 * @param <T>
	 *            type of the value to be validated
	 * @param widgetReader
	 *            read the value from the widget.
	 * @param left
	 *            left value, <code>null</code> means not check
	 * @param leftIncluded
	 *            <code>true</code> if left side inclusive
	 * @param right
	 *            right value, <code>null</code> means not check
	 * @param rightIncluded
	 *            <code>true</code> if right side inclusive
	 * @return created rule
	 */
	public static final <T extends Comparable<T>> RangeRule<T> between(
			WidgetReader<T> widgetReader, T left, boolean leftIncluded,
			T right, boolean rightIncluded) {
		RangeRule<T> result = new RangeRule<T>();
		result.mWidgetReader = widgetReader;
		result.mLeft = left;
		result.mLeftIncluded = leftIncluded;
		result.mRight = right;
		result.mRightIncluded = rightIncluded;
		return result;
	}

	private T mLeft;

	private boolean mLeftIncluded = true;

	private T mRight;

	private boolean mRightIncluded = true;

	private WidgetReader<T> mWidgetReader;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.softao.ming.gwt.user.client.ValidationRule#execute(com.google.gwt
	 * .user.client.ui.Widget)
	 */
	public boolean execute(Widget widget) {
		if (mLeft == null) {
			return true;
		}

		WidgetReader<T> reader = this.mWidgetReader;

		if (reader == null) {
			return false;
		}

		T value = reader.read(widget);
		boolean result = false;
		if (mLeft != null) {
			if (this.mLeftIncluded) {
				result = mLeft.compareTo(value) <= 0;
			} else {
				result = mLeft.compareTo(value) < 0;
			}
		}
		if (result && mRight != null) {
			if (this.mRightIncluded) {
				result = mRight.compareTo(value) <= 0;
			} else {
				result = mRight.compareTo(value) < 0;
			}
		}
		return result;
	}

	/**
	 * @return the left
	 */
	public T getLeft() {
		return mLeft;
	}

	/**
	 * @return the right
	 */
	public T getRight() {
		return mRight;
	}

	/**
	 * @return the leftIncluded
	 */
	public boolean isLeftIncluded() {
		return mLeftIncluded;
	}

	/**
	 * @return the rightIncluded
	 */
	public boolean isRightIncluded() {
		return mRightIncluded;
	}

	/**
	 * @param left
	 *            the left to set
	 */
	public void setLeft(T left) {
		mLeft = left;
	}

	/**
	 * @param leftIncluded
	 *            the leftIncluded to set
	 */
	public void setLeftIncluded(boolean leftIncluded) {
		mLeftIncluded = leftIncluded;
	}

	/**
	 * @param right
	 *            the right to set
	 */
	public void setRight(T right) {
		mRight = right;
	}

	/**
	 * @param rightIncluded
	 *            the rightIncluded to set
	 */
	public void setRightIncluded(boolean rightIncluded) {
		mRightIncluded = rightIncluded;
	}

	/**
	 * @param widgetReader the widgetReader to set
	 */
	public void setWidgetReader(WidgetReader<T> widgetReader) {
		mWidgetReader = widgetReader;
	}

	/**
	 * @return the widgetReader
	 */
	public WidgetReader<T> getWidgetReader() {
		return mWidgetReader;
	}

}
